package com.usoft.thrift.benchmark;

import com.usoft.thrift.benchmark.util.Utils;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

import static java.util.Arrays.asList;

/**
 * Created by xinxingegeya on 16/2/25.
 */
public class ClientConfiguration implements Configuration {

    ClientTransport transport = ClientTransport.T_SOCKET;
    boolean ssl;
    SocketAddress address;
    //未完成的
    int duration = 60;
    // seconds
    int warmupDuration = 10;
    String histogramFile;
    int clientNums = 10;
    int outstandingRpcsPerClient = 10;
    ProtocolType protocolType = ProtocolType.T_COMPACT_PROTOCOL;
    ClientType clientType = ClientType.SYNC;

    Server server = Server.T_NONBLOCKING_SERVER;

    /**
     * Constructs a builder for configuring a client application with supported
     * parameters. If no
     * parameters are provided, all parameters are assumed to be supported.
     */
    static Builder newBuilder(ClientParam... supportedParams) {
        return new Builder(supportedParams);
    }

    static class Builder
        extends AbstractConfigurationBuilder<ClientConfiguration> {
        private final Collection<Param> supportedParams;

        private Builder(ClientParam... supportedParams) {
            this.supportedParams = supportedOptionsSet(supportedParams);
        }

        @Override
        protected ClientConfiguration newConfiguration() {
            return new ClientConfiguration();
        }

        @Override
        protected Collection<Param> getParams() {
            return supportedParams;
        }

        @Override
        protected ClientConfiguration build0(ClientConfiguration config) {
            if (config.ssl) {
                if (!config.transport.tlsSupported) {
                    throw new IllegalArgumentException(
                        "Transport " + config.transport.name().toLowerCase()
                            + " does not support TLS.");
                }

                if (config.address instanceof InetSocketAddress) {
                    // Override the socket address with the host from the testca.
                    InetSocketAddress address = (InetSocketAddress) config.address;
                    config.address = Utils.testServerAddress(
                        address.getHostName(), address.getPort());
                }
            }

            // Verify that the address type is correct for the transport type.
            config.transport.validateSocketAddress(config.address);

            return config;
        }

        private static Set<Param> supportedOptionsSet(
            ClientParam... supportedParams) {
            if (supportedParams.length == 0) {
                // If no options are supplied, default to including all options.
                supportedParams = ClientParam.values();
            }
            return Collections.unmodifiableSet(
                new LinkedHashSet<Param>(asList(supportedParams)));
        }
    }

    enum ClientType {
        SYNC,
        ASYNC
    }

    /**
     * All of the supported transports.
     */
    enum ClientTransport {
        T_SOCKET(true, "客户端套接字", SocketAddressValidator.INET),
        T_NONBLOCKING_SOCKET(true, "客户端非阻塞套接字", SocketAddressValidator.INET),
        T_HTTP_CLIENT(true, "使用HTTP协议", SocketAddressValidator.INET);

        final boolean tlsSupported;
        final String description;
        final SocketAddressValidator socketAddressValidator;

        ClientTransport(boolean tlsSupported, String description,
                        SocketAddressValidator socketAddressValidator) {
            this.tlsSupported = tlsSupported;
            this.description = description;
            this.socketAddressValidator = socketAddressValidator;
        }

        /**
         * Validates the given address for this transport.
         *
         * @throws IllegalArgumentException if the given address is invalid for this transport.
         */
        void validateSocketAddress(SocketAddress address) {
            if (!socketAddressValidator.isValidSocketAddress(address)) {
                throw new IllegalArgumentException(
                    "Invalid address " + address + " for transport " + this);
            }
        }

        static String getDescriptionString() {
            StringBuilder builder = new StringBuilder(
                "Select the transport to use. Options:\n");
            boolean first = true;
            for (ClientTransport transport : ClientTransport.values()) {
                if (!first) {
                    builder.append("\n");
                }
                builder.append(transport.name().toLowerCase());
                builder.append(": ");
                builder.append(transport.description);
                first = false;
            }
            return builder.toString();
        }
    }

    enum ClientParam implements AbstractConfigurationBuilder.Param {
        ADDRESS("STR", "Socket address (host:port)", null, true) {
            @Override
            protected void setClientValue(ClientConfiguration config,
                                          String value) {
                config.address = Utils.parseSocketAddress(value);
            }
        },
        SERVER("STR", "指定服务器端类型", null, true) {
            @Override
            protected void setClientValue(ClientConfiguration config,
                                          String value) {
                Server server = Enum.valueOf(Server.class, value);
                config.server = server;

            }
        },
        TRANSPORT("STR", "thrift transport", null, true) {
            @Override
            protected void setClientValue(ClientConfiguration config,
                                          String value) {
                ClientTransport transport = Enum.valueOf(ClientTransport.class,
                    value);
                config.transport = transport;
            }
        },
        PROTOCOL("STR", "thrift protocol", null, true) {
            @Override
            protected void setClientValue(ClientConfiguration config,
                                          String value) {
                ProtocolType protocolType = Enum.valueOf(ProtocolType.class,
                    value);
                config.protocolType = protocolType;
            }
        };

        private final String type;
        private final String description;
        private final String defaultValue;
        private final boolean required;

        ClientParam(String type, String description, String defaultValue) {
            this(type, description, defaultValue, false);
        }

        ClientParam(String type, String description, String defaultValue,
                    boolean required) {
            this.type = type;
            this.description = description;
            this.defaultValue = defaultValue;
            this.required = required;
        }

        @Override
        public String getName() {
            return name().toLowerCase();
        }

        @Override
        public String getType() {
            return type;
        }

        @Override
        public String getDescription() {
            return description;
        }

        @Override
        public String getDefaultValue() {
            return defaultValue;
        }

        @Override
        public boolean isRequired() {
            return required;
        }

        @Override
        public void setValue(Configuration config, String value) {
            setClientValue((ClientConfiguration) config, value);
        }

        protected abstract void setClientValue(ClientConfiguration config, String value);
    }

}
